/*
 *   实用工具类模块
 */
#ifndef __BUC_UTIL__
#define __BUC_UTIL__

#include "bundle.h"
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>
#include <sys/stat.h>
#include <unistd.h>

// 编码转换库
#include <iconv.h>
#include <memory>

// 引入实验性库experimental中的filesystem库 对文件夹进行操作
// 重命名命名空间
namespace fs = std::experimental::filesystem;

namespace cloud
{
    class FileUtil
    {
    private:
        std::string _filepath; // 文件路径
    private:
        bool FileStat(struct stat *st) // 获取文件状态
        {
            if (st == nullptr)
                return false;
            if (stat(_filepath.c_str(), st) != 0)
            {
                std::cout << "FileStat(): file status error!" << std::endl;
                return false;
            }
            return true;
        }

    public:
        FileUtil(const std::string &filepath) : _filepath(filepath) {}
        size_t FileSize() // 获取文件大小
        {
            struct stat st;
            if (!FileStat(&st))
                return 0; // 文件不存在和文件内容都为0都输出0
            return st.st_size;
        }
        std::string FileName() // 获取文件名称(从路径中截取)
        {
            auto pos = _filepath.find_last_of("\\/");
            if (pos == std::string::npos)
                return _filepath;
            return _filepath.substr(pos + 1);
        }

        time_t FileModTime() // 获取文件修改时间
        {
            struct stat st;
            if (!FileStat(&st))
                return -1;
            return st.st_mtime;
        }
        time_t FileAccTime() // 获取文件访问时间
        {
            struct stat st;
            if (!FileStat(&st))
                return -1;
            return st.st_atime;
        }

        bool SetContent(const std::string &body) // 向文件中追加写入内容
        {
            std::ofstream ofs;
            ofs.open(_filepath, std::ios::binary | std::ios::app | std::ios::out);
            if (ofs.is_open() == false)
            {
                // 如果文件名为空 则为空文件
                std::cout << "SetContent(): open " << (body.size() == 0 ? "NULL FILE!" : body) << " error!" << std::endl;
                return false;
            }
            ofs.write(body.c_str(), body.size());
            if (ofs.good() == false)
            {
                std::cout << "SetContent(): write file error!" << std::endl;
                ofs.close();
                return false;
            }
            return true;
        }

        bool SetTruncContent(const std::string &body) // 向文件中写入内容 清空原有内容
        {
            std::ofstream ofs;
            ofs.open(_filepath, std::ios::binary | std::ios::trunc | std::ios::out);
            if (ofs.is_open() == false)
            {
                // 如果文件名为空 则为空文件
                std::cout << "SetTruncContent(): open " << (body.size() == 0 ? "NULL FILE!" : body) << " error!" << std::endl;
                return false;
            }
            ofs.write(body.c_str(), body.size());
            if (ofs.good() == false)
            {
                std::cout << "SetTruncContent(): write file error!" << std::endl;
                ofs.close();
                return false;
            }
            return true;
        }

        bool GetPosLen(std::string *body, size_t pos, size_t len) // 从文件的指定位置获取指定长度的内容
        {
            if (body == nullptr)
            {
                std::cout << "GetPosLen(): body null pointer!" << std::endl;
                return false;
            }
            size_t fsize = this->FileSize();
            if (fsize == -1)
                return false;
            if (pos + len > fsize)
            {
                std::cout << "GetPosLen(): the read length is out of bounds!" << std::endl;
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filepath, std::ios::binary | std::ios::in);
            if (ifs.is_open() == false)
            {
                std::cout << "SetTruncContent(): open file error!" << std::endl;
                return false;
            }
            ifs.seekg(pos, std::ios::beg);
            body->resize(fsize);
            ifs.read(&(*body)[0], fsize);
            if (ifs.good() == false)
            {
                std::cout << "SetTruncContent(): read file error!" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        bool GetContent(std::string *body) // 获取文件内容
        {
            return GetPosLen(body, 0, FileSize());
        }

        bool Compress(std::string &packname, unsigned int type = bundle::LZIP) // 将本文件内容压缩 写入packname中
        {
            // 1.读取文件内容
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "Compress(): GetContent " << this->FileName() << "error!" << std::endl;
                return false;
            }
            // 2.压缩文件
            body = bundle::pack(type, body);
            // 3.将压缩内容写入packname
            FileUtil pf(packname);
            if (pf.SetTruncContent(body) == false)
            {
                std::cout << "Compress(): SetContent" << this->FileName() << "error!" << std::endl;
                return false;
            }
            return true;
        }
        bool UnCompress(std::string &filename) // 将本文件中已经压缩的内容解压 写入filename中
        {
            // 1.读取文件内容
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "UnCompress(): GetContent" << this->FileName() << "error!" << std::endl;
                return false;
            }
            // 2.解压文件
            body = bundle::unpack(body);
            // 3.写入文件
            FileUtil upf(filename);
            if (upf.SetContent(body) == false)
            {
                std::cout << "UnCompress(): SetContent" << this->FileName() << "error!" << std::endl;
                return false;
            }
            return true;
        }

        bool FileRemove()
        {
            // 文件不存在 则不删除
            if (Exists() == false)
                return true;
            remove(_filepath.c_str());
            return true;
        }

        bool Exists() // 判断 文件/目录 是否存在
        {
            return fs::exists(_filepath);
        }

        bool CreateDirectory() // 创建目录
        {
            if (this->Exists())
                return true;
            // 创建多级目录
            return fs::create_directories(_filepath);
        }
        bool ScanDirectory(std::vector<std::string> *array) // 获取目录下的所有文件名称(没有目录) - 浏览目录下除目录以外的文件
        {
            if (Exists() == false)
            {
                std::cout << "\"" << FileName() << "\" directory not exist!" << std::endl;
                return false;
            }
            // 使用directory_iterator目录迭代器遍历目录
            for (auto &p : fs::directory_iterator(_filepath))
            {
                // 如果是目录 则不入array
                if (fs::is_directory(p))
                    continue;
                // 写入文件的相对路径
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    };

    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root, std::string *str) // 序列化
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> writes(swb.newStreamWriter());

            std::stringstream ss;
            if (writes->write(root, &ss) != 0)
            {
                std::cout << "Serialize(): StreamWriter.write error!" << std::endl;
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> reads(crb.newCharReader());
            std::string errs;
            if (reads->parse(str.c_str(), str.c_str() + str.size(), root, &errs) == false)
            {
                std::cout << "parse error: " << errs << std::endl;
                return false;
            }
            return true;
        }
    };

    class EncodingConver
    {
        #define GBK "gb2312"
        #define UTF8 "utf-8"
    public:
        static std::string GBKToUTF8(const std::string &gbk)
        {
            return converHelper(GBK, UTF8, gbk);
        }

        static std::string UTF8ToGBK(const std::string &utf8)
        {
            return converHelper(UTF8, GBK, utf8);
        }

    private:
        static std::string converHelper(const char *fromcode, const char *tocode, const std::string &code)
        {
            size_t cz = code.size();
            char *pc = const_cast<char *>(&code[0]);

            size_t tz = 1000000;
            char tmp[tz] = {0};
            int ret = converHelper(fromcode, tocode, pc, &cz, tmp, &tz);
            if (ret == -1)
            {
                std::cerr << "Conver Fail!" << std::endl;
                return "";
            }

            return tmp; // 自动构造string对象
        }

        static int converHelper(
            const char *fromcode,
            const char *tocode,
            char *in, size_t *insize, char *out, size_t *outsize)
        {
            // 创建编码转换的句柄
            iconv_t cd = iconv_open(tocode, fromcode);
            if (cd == (iconv_t *)-1)
            {
                std::cerr << "Open iconv fail!" << std::endl;
                return -1;
            }

            char **pi = &in;
            char **po = &out;

            // 进行编码转换
            size_t ret = iconv(cd, pi, insize, po, outsize);

            if (ret == (size_t)-1)
            {
                std::cerr << "iconv fail!" << std::endl;
                iconv_close(cd);
                return -1;
            }

            // 关闭句柄
            if (iconv_close(cd))
            {
                std::cerr << "Close iconv fail!" << std::endl;
                return -1;
            }
        }
    };

}

#endif